package cn.sansi.plugin.container;

import cn.sansi.common.exception.BizException;
import cn.sansi.plugin.AbstractPlugin;
import cn.sansi.plugin.Configuration;
import cn.sansi.plugin.constant.PluginErrorCode;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * 插件加载器，
 */
public class LoadUtil {
    private static final String pluginTypeNameFormat = "plugin.%s";

    private LoadUtil()
    {
        pluginHomePath=CoreConstant.PLUGIN_HOME;
    }

    private static String pluginHomePath;



    /**
     * jarLoader的缓冲
     */
    private static final Map<String, JarLoader> jarLoaderCenter = new HashMap<>();

    private static String generatePluginKey(String pluginName) {
        return String.format(pluginTypeNameFormat, pluginName);
    }

    /**
     * 加载taskPlugin，reader、writer都可能加载
     *
     * @param pluginName 插件名称
     * @param configValue 插件扩展配置值
     * @return 插件实例
     */
    public static AbstractPlugin loadPlugin(String pluginName,String configValue) {
        try {
            Configuration config = buildConfiguration(pluginName,configValue);
            Class<? extends AbstractPlugin> clazz = LoadUtil.loadPluginClass(config);
            AbstractPlugin taskPlugin = clazz.newInstance();
            taskPlugin.setPluginConf(config);
            taskPlugin.init();
            return taskPlugin;
        } catch (Exception e) {
            throw BizException.asBizException(PluginErrorCode.RUNTIME_ERROR,
                    String.format("不能找plugin[%s]的配置.",
                            pluginName), e);
        }
    }

    /**
     * 下载插件
     * @param pluginName 插件名称
     */
    public static void unloadPlugin(String pluginName) {
        String jarLoaderKey = generatePluginKey(pluginName);
        JarLoader jarLoader = jarLoaderCenter.get(jarLoaderKey);
        if (null != jarLoader) {
            jarLoaderCenter.remove(jarLoaderKey);
        }
    }

    /**
     * 反射出具体plugin实例
     *
     * @param config 插件配置
     * @return 插件类型
     */
    @SuppressWarnings("unchecked")
    private static synchronized Class<? extends AbstractPlugin> loadPluginClass(Configuration config) {
        JarLoader jarLoader = LoadUtil.getJarLoader(config.getString("name"));
        try {
            return (Class<? extends AbstractPlugin>) jarLoader
                    .loadClass(config.getString("class"));
        } catch (Exception e) {
            throw BizException.asBizException(PluginErrorCode.RUNTIME_ERROR, e);
        }
    }

    public static synchronized JarLoader getJarLoader(String pluginName) {
        File pluginPath =getPluginDir(pluginName);
        String jarLoaderKey = generatePluginKey(pluginName);
        JarLoader jarLoader = jarLoaderCenter.get(jarLoaderKey);
        if (null == jarLoader) {
            if (!pluginPath.exists() || pluginPath.isFile()) {
                throw BizException.asBizException(
                        PluginErrorCode.RUNTIME_ERROR,
                        String.format(
                                "插件[%s]路径非法!", pluginName));
            }
            jarLoader = new JarLoader(new String[]{pluginPath.getAbsolutePath()});
            jarLoaderCenter.put(jarLoaderKey, jarLoader);
        }
        return jarLoader;
    }

    public static void setPluginHomePath(String pluginHomePath) {
        LoadUtil.pluginHomePath = pluginHomePath;
    }

    public static File getPluginDir(String pluginName) {
        if (StringUtils.isBlank(pluginHomePath)) {
            pluginHomePath = CoreConstant.PLUGIN_HOME;
        }
        File pluginPath = new File( pluginHomePath,pluginName);
        return pluginPath;
    }


    public static Configuration buildConfiguration(String pluginName,String configValue){
        File pluginPath =getPluginDir(pluginName);
        File configFile = new File(pluginPath, "plugin.json");
        if (!configFile.exists()) {
            throw BizException.asBizException(PluginErrorCode.RUNTIME_ERROR,
                    String.format("插件[%s]配置文件不存在!", pluginName));
        }
        Configuration config = Configuration.from(configFile);
        if(StringUtils.isNotBlank(configValue)){
            try {
                Configuration tmpConfig= Configuration.from(configValue);
                config= config.merge(tmpConfig,false);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return config;
    }
}
